﻿using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.IO;

namespace SmartCard.Framework
{
	/// <summary>
	/// Collection of features supported by the current card.  This class performs a lazy evaluation to
	/// detect if a feature is supported, so if iterating over the entire set of features, the card will
	/// have to be queried to determine if it supported each feature.  Some features may have higher cost
	/// associated with the detection process.
	/// </summary>
	public class MefFeatureCollection : IFeatureCollection
	{
		private Dictionary<Type, Lazy<ICardFeature>> _features;

		#region properties

		public ICardFeature this[Type key]
		{
			get
			{
				return _features[key].Value;
			}
		}

		#endregion

		#region construction

		public MefFeatureCollection(ICard card, DirectoryInfo directory)
		{
			if (directory == null)
			{ throw new ArgumentNullException("directory"); }

			if (card == null)
			{ throw new ArgumentNullException("card"); }

			Compose(card, directory);
		}

		public MefFeatureCollection(ICard card)
			: this(card, new FileInfo(
				new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath).Directory)
		{ }

		#endregion

		#region publics

		public IEnumerator<ICardFeature> GetEnumerator()
		{
			foreach (var feature in _features.Values)
			{
				if (feature.Value.IsSupported())
				{ yield return feature.Value; }
			}
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		#region privates

		private void Compose(ICard card, DirectoryInfo directory)
		{
			using (AggregateCatalog catalog = new AggregateCatalog())
			using (var container = new CompositionContainer(catalog))
			{
				catalog.Catalogs.Add(new DirectoryCatalog(directory.FullName));
				var cardFeatures = container.GetExports<ICardFeature>();

				_features = new Dictionary<Type, Lazy<ICardFeature>>();
				foreach (var cardFeature in cardFeatures)
				{
					_features.Add(cardFeature.Value.GetType(), cardFeature);
					cardFeature.Value.Card = card;
				}
			}
		}

		#endregion
	}
}
